package github.flandre.examplemod.common.energy.tileentity;

import com.mojang.datafixers.DSL;
import github.flandre.examplemod.common.energy.SimpleEnergyNetwork;
import github.flandre.examplemod.common.energy.block.FEDemoMachineBlock;
import github.flandre.examplemod.common.energy.block.FEDemoWireBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.energy.IEnergyStorage;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.registries.ObjectHolder;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nonnull;
import java.util.Objects;

@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
public class FEDemoWireTileEntity extends TileEntity
{
    public static final String NAME = "examplemod:wire";

    @ObjectHolder(NAME)
    public static TileEntityType<FEDemoWireTileEntity> TILE_ENTITY_TYPE;

    @SubscribeEvent
    public static void onRegisterTileEntityType(@Nonnull RegistryEvent.Register<TileEntityType<?>> event)
    {
        event.getRegistry().register(TileEntityType.Builder.create(FEDemoWireTileEntity::new, FEDemoWireBlock.BLOCK).build(DSL.remainderType()).setRegistryName(NAME));
    }

    private FEDemoWireTileEntity()
    {
        super(TILE_ENTITY_TYPE);
    }

    private final LazyOptional<IEnergyStorage> lazyOptional = LazyOptional.of(() -> new IEnergyStorage()
    {
        private final SimpleEnergyNetwork network = SimpleEnergyNetwork.Factory.get(FEDemoWireTileEntity.this.world);

        @Override
        public int receiveEnergy(int maxReceive, boolean simulate)
        {
            int energy = this.getEnergyStored();
            int diff = Math.min(500, Math.min(this.getMaxEnergyStored() - energy, maxReceive));
            if (!simulate)
            {
                this.network.addEnergy(FEDemoWireTileEntity.this.pos, diff);
                if (diff != 0)
                {
                    FEDemoWireTileEntity.this.markDirty();
                }
            }
            return diff;
        }

        @Override
        public int extractEnergy(int maxExtract, boolean simulate)
        {
            int energy = this.getEnergyStored();
            int diff = Math.min(500, Math.min(energy, maxExtract));
            if (!simulate)
            {
                this.network.addEnergy(FEDemoWireTileEntity.this.pos, -diff);
                if (diff != 0)
                {
                    FEDemoWireTileEntity.this.markDirty();
                }
            }
            return diff;
        }

        @Override
        public int getEnergyStored()
        {
            return Math.min(this.getMaxEnergyStored(), this.network.getNetworkEnergy(FEDemoWireTileEntity.this.pos));
        }

        @Override
        public int getMaxEnergyStored()
        {
            return 1_000 * this.network.getNetworkSize(FEDemoWireTileEntity.this.pos);
        }

        @Override
        public boolean canExtract()
        {
            return true;
        }

        @Override
        public boolean canReceive()
        {
            return true;
        }
    });

    @Nonnull
    @Override
    public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, Direction side)
    {
        boolean isEnergy = Objects.equals(cap, CapabilityEnergy.ENERGY);
        return isEnergy ? this.lazyOptional.cast() : super.getCapability(cap, side);
    }


    private Integer tmpEnergy = null;

    @Override
    public void read(@NotNull BlockState state, CompoundNBT compound) {
        this.tmpEnergy = compound.getInt("WireEnergy");
        super.read(state, compound);
    }


    @Nonnull
    @Override
    public CompoundNBT write(@Nonnull CompoundNBT compound)
    {
        SimpleEnergyNetwork network = SimpleEnergyNetwork.Factory.get(this.world);
        compound.putInt("WireEnergy", network.getSharedEnergy(this.pos));
        return super.write(compound);
    }

    @Override
    public void onLoad()
    {
        if (this.world != null && !this.world.isRemote)
        {
            SimpleEnergyNetwork network = SimpleEnergyNetwork.Factory.get(this.world);
            if (this.tmpEnergy != null)
            {
                int diff = this.tmpEnergy - network.getSharedEnergy(this.pos);
                network.addEnergy(this.pos, diff);
                this.tmpEnergy = null;
            }
            network.enableBlock(this.pos, this::markDirty);
        }
        super.onLoad();
    }

    @Override
    public void onChunkUnloaded()
    {
        if (this.world != null && !this.world.isRemote)
        {
            SimpleEnergyNetwork network = SimpleEnergyNetwork.Factory.get(this.world);
            network.disableBlock(this.pos, this::markDirty);
        }
        super.onChunkUnloaded();
    }

    @Override
    public void remove()
    {
        if (this.world != null && !this.world.isRemote)
        {
            SimpleEnergyNetwork network = SimpleEnergyNetwork.Factory.get(this.world);
            network.disableBlock(this.pos, () ->
            {
                int diff = network.getSharedEnergy(this.pos);
                network.addEnergy(this.pos, -diff);
                this.markDirty();
            });
        }
        super.remove();
    }
}
